﻿using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Windows.Forms;
using System.Collections.Generic;
using GMusicBox.Core;
using GMusicBox.Core.Setting;

namespace GMusicBox
{
    public class LyricShow : Control
    {
        #region 构造函数
        public LyricShow()
        {
            fdl.SizeChanged += new EventHandler(fdl_SizeChanged);
            this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
            lyric = new Lyric { IsNull = true };
        }
        #endregion
        #region 变量
        #region 属性变量
        double current, duration;
        Lyric lyric; string lpath;
        internal bool hid = false;
        #endregion
        #region 配置相关
        public Color[][] defaults = new Color[][] { new Color[]{Color.FromArgb(144, 210, 248), Color.FromArgb(55, 137, 221), Color.Yellow, Color.FromArgb(255, 232, 137) },
        new Color[] { Color.FromArgb(37, 152, 10), Color.FromArgb(129, 249, 0), Color.FromArgb(255, 246, 0), Color.FromArgb(255, 120, 0) },
        new Color[] { Color.FromArgb(94, 16, 223), Color.FromArgb(216, 147, 255), Color.FromArgb(255, 206, 223), Color.FromArgb(255, 60, 133) }};
        #endregion
        #region 其他功能变量
        internal Rectangle[] bounds = new Rectangle[0];
        private Point mouse_offset;
        private int nowline = 0;
        internal int drag = 0;
        internal bool dragging = false;
        public frmDesktopLyric fdl = new frmDesktopLyric();
        delegate void SetLyricCallBack(Lyric l);
        #endregion
        public event EventHandler ProcessChangedFromDrag;
        public event CancelEventHandler PreLyricDrag;
        public event CancelEventHandler PreDraggingLyric;
        #endregion
        #region 属性
        public bool Horizontal { get; set; }
        public bool AutoRefresh { get; set; }

        bool est = false;
        [DefaultValue(false)]
        public bool EnabledShowText
        {
            get { return est; }
            set { est = value; }
        }

        public double Duration
        {
            get { return duration; }
            set
            {
                try
                {
                    duration = value;
                    if (lyric == null) return;

                    if (Path.GetExtension(lyric.Source).ToLower() == ".txt")
                    {
                        StreamReader sr = new StreamReader(lyric.Source);
                        string[] s = sr.ReadToEnd().Trim().Split('\n');
                        sr.Close();

                        string[] ltl = new string[s.Length + 1];
                        double[] ltn = new double[s.Length + 1];

                        ltl[0] = "抱歉，该歌词没有同步信息，系统自动匹配的将会出现不准确的情况。";
                        ltn[0] = 0;
                        for (int i = 1; i < ltl.Length; i++)
                        {
                            ltl[i] = s[i - 1];
                            ltn[i] = duration / s.Length * i;
                        }

                        Lyric l = new Lyric();
                        l.Texts.AddRange(ltl);
                        l.Times.AddRange(ltn);
                        l.Source = lyric.Source;
                        lyric = l;
                        this.SetLyric(lyric);
                        SetLyric(lyric);
                    }
                    else
                    {
                        lyric.Times[lyric.Times.Count - 1] = duration;
                    }
                }
                catch { }
            }
        }

        public ContextMenu DContextMenu
        {
            get { return fdl.ContextMenu; }
            set { fdl.ContextMenu = value; }
        }

        public bool DVisible
        {
            get { return hid; }
        }

        public Lyric CurrentLyric
        {
            get { return lyric; }
        }

        public string LyricPath
        {
            get { return lpath; }
            set
            {
                lpath = value;
                current = 0;
                if (!System.IO.File.Exists(lpath)) return;
                SetLyric(new Lyric(lpath));
            }
        }

        public double CurrentProgress
        {
            get { return current; }
            set
            {
                current = value;

                if (lyric != null)
                    for (int i = 0; i < lyric.Texts.Count; i++)
                        if (i != lyric.Texts.Count - 1 && current >= lyric.Times[i] && current < lyric.Times[i + 1])
                        { nowline = i; break; }

                if (AutoRefresh && !dragging) this.Invalidate();
            }
        }
        #endregion
        #region 自定义函数
        public void ShowText(string s)
        {
            if (lyric == null || lyric.IsNull)
            {
                lyric = new Lyric { IsNull = lyric.IsNull };
                lyric.Texts.Add(s); lyric.Texts.Add("");
                lyric.Times.Add(0); lyric.Times.Add(9999);
            }
            else lyric.Texts[0] = s;

            fdl.ClearCache();
            RefreshDesktopLyric();
            this.SetLyric(lyric);
            //this.EnabledShowText = true;
            try { this.Text = s; }
            catch { }
        }

        internal void SetLyric(Lyric l)
        {
            if (this.InvokeRequired) this.Invoke(new SetLyricCallBack(SetLyric), l);
            else
            {
                lyric = l;
                if (l == null)
                {
                    lyric = new Lyric { IsNull = true };
                    CurrentProgress = 0;
                    this.EnabledShowText = true;
                    return;
                }

                current = 0;
                if (lyric.Times != null && lyric.Texts != null)
                {
                    bounds = new Rectangle[lyric.Times.Count];
                    for (int i = 0; i < lyric.Texts.Count; i++)
                    {
                        bounds[i] = new Rectangle();
                        string s = lyric.Texts[i];
                        bounds[i].Size = new Size(this.Width - 10, this.Font.Height + 4);
                        if (TextRenderer.MeasureText(s, this.Font).Width > (this.Width - 10) * 2) bounds[i].Height *= 3;
                        else if (TextRenderer.MeasureText(s, this.Font).Width > this.Width - 10) bounds[i].Height *= 2;
                        bounds[i].Location = new Point(0, i != 0 ? bounds[i - 1].Bottom + 5 : this.Height / 2 - bounds[i].Height * 2);
                    }
                }
                RefreshDesktopLyric();
                this.EnabledShowText = l.IsNull;
                this.Invalidate();
            }
        }

        internal bool Penetrate()
        {
            fdl.Penetrate();
            return fdl.trans;
        }

        public void OpenOrCloseDesktopLyric()
        {
            if (hid) { hid = false; fdl.Show(); RefreshDesktopLyric(); }
            else { hid = true; fdl.Hide(); }
        }

        public void SetDefault(int i)
        {
            Color[] cs = defaults[i];
            LyricShowSetting.DTextColor = cs[0];
            LyricShowSetting.DTextColor1 = cs[1];
            LyricShowSetting.DHTextColor = cs[2];
            LyricShowSetting.DHTextColor1 = cs[3];

            if (lyric.Texts.Count == 0)
            {
                lyric = new Lyric { IsNull = true };
                lyric.Texts.Add("");
                lyric.Times.Add(0);
            }

            UpdateSkin();
        }

        public void UpdateSkin()
        {
            string[] dfont = LyricShowSetting.DFont.Split(',');
            int w = fdl.Width;
            Font f = new Font(dfont[0], float.Parse(dfont[1]), (FontStyle)(int.Parse(dfont[2])));
            fdl.Font = f;
            fdl.Width = w;
            fdl.Height = f.Height;
            fdl.ClearCache();
            fdl.ResetLocation();

            dfont = LyricShowSetting.LFont.Split(',');
            f = new Font(dfont[0], float.Parse(dfont[1]), (FontStyle)(int.Parse(dfont[2])));
            this.Font = f;

            if (lyric.Texts.Count == 0)
            {
                lyric.IsNull = true;
                lyric.Texts.Add("");
                lyric.Times.Add(0);
            }

            this.Invalidate();
            RefreshDesktopLyric();
        }

        public void ShowDesktopLyric(bool justInit)
        {
            if (!justInit) fdl.Show();
            else hid = true;
            fdl.Width = MainSetting.LastDesktopLyricWidth;
            fdl.Location = MainSetting.LastDesktopLyricLocation;
        }

        public void ResetDesktopLyric()
        {
            fdl.SetBitmap(new Bitmap(1, 1));
        }

        public void RefreshDesktopLyric()
        {
            if (lyric.Texts.Count == 0 || hid) return;
            
            int i = nowline;
            double timecb = 0;

            if (i >= lyric.Texts.Count) i = 0;
            if (LyricShowSetting.DKalaOK)
            {
                if (!lyric.IsNull)
                {
                    double timec = lyric.Times[i + 1] - lyric.Times[i];
                    double timec1 = current - lyric.Times[i];
                    timecb = timec1 / timec;
                }
            }
            else timecb = 1;

            if (lyric.Texts[i] == "抱歉，无法获取歌词内容或不存在此歌曲的歌词") { i = 0; timecb = 0; }
            fdl.ClearCache();
            fdl.DrawText(lyric.Texts[i]);
            fdl.DrawToVisual(timecb, (byte)(LyricShowSetting.DOpacity * 255));
        }

        public void CustomDesktopLyric(string text)
        {
            ShowText(text);
        }

        public void PaintControl(Graphics g)
        {
            OnPaint(new PaintEventArgs(g, this.Bounds));
        }

        private double GetNumberTime(string s)
        {
            try
            {
                string[] ss = s.Split('.', ':');
                int t, t1, t2;
                if (!int.TryParse(ss[0], out t)) return 0;
                if (!int.TryParse(ss[1], out t1)) return 0;
                if (!int.TryParse(ss[2], out t2)) return 0;
                t *= 60;
                t1 = t + int.Parse(ss[1]);
                return t1 + t2 / 100.0;
            }
            catch { }
            return 0;
        }

        private string GetStringTime(double time)
        {
            try
            { return ((int)(time / 60)).ToString() + ":" + ((int)(time % 60)).ToString() + "." + ((int)((time - (int)time) * 100)).ToString(); }
            catch { }
            return "";
        }

        public double GetTimeFromLocation(int Y)
        {
            for (int i = 0; i < bounds.Length; i++)
            {
                if (i != bounds.Length - 1)
                {
                    if (Y >= bounds[i].Y && Y < bounds[i + 1].Y)
                    {
                        //两句歌词的位置差
                        double topc = bounds[i + 1].Y - bounds[i].Y + bounds[i].Height / 2 - bounds[i + 1].Height / 2;
                        double temp = Y - bounds[i].Y; //多出的位置
                        double timecb = temp / topc; //已经过的百分比
                        double timec = lyric.Times[i + 1] - lyric.Times[i]; //时间差
                        return lyric.Times[i] + timec * timecb; //得出结果
                    }
                }
            }
            return 0;
        }
        #endregion
        #region 事件
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            if (Horizontal || lyric == null) return;
            if (PreLyricDrag != null)
            {
                CancelEventArgs c = new CancelEventArgs(false);
                PreLyricDrag(this, c);
                if (c.Cancel) return;
            }

            if (lyric.IsNull) return;
            this.Cursor = new Cursor(res.dragging.GetHicon());
            mouse_offset = mouse_offset = new Point(e.X, e.Y);
            dragging = true;
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);
            if (e.Button == MouseButtons.Left && dragging && !Horizontal && lyric != null)
            {
                if (PreDraggingLyric != null)
                {
                    CancelEventArgs c = new CancelEventArgs(false);
                    PreDraggingLyric(this, c);
                    if (c.Cancel) return;
                }

                Point mousePos = e.Location;
                drag += -(mousePos.Y - mouse_offset.Y);
                if (drag < 0) drag = 0;
                CurrentProgress = GetTimeFromLocation(drag);
                if (ProcessChangedFromDrag != null) ProcessChangedFromDrag(this, null);
                this.Invalidate();
                mouse_offset = e.Location;
            }
        }

        protected override void OnMouseEnter(EventArgs e)
        {
            base.OnMouseEnter(e);
            if (Horizontal) this.Cursor = Cursors.Default;
            else this.Cursor = new Cursor(res.drag.GetHicon());
        }

        protected override void OnMouseUp(MouseEventArgs e)
        {
            base.OnMouseUp(e);
            if (Horizontal) this.Cursor = Cursors.Default;
            else
            {
                this.Cursor = new Cursor(res.drag.GetHicon());
                dragging = false;
            }
            this.Invalidate();
        }

        private void fdl_SizeChanged(object sender, EventArgs e)
        {
            try { RefreshDesktopLyric(); }
            catch { }
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (lyric == null) return;
            int temp = 0; double timecb = 0;

            StringFormat sf = new StringFormat();
            sf.Alignment = StringAlignment.Center;
            sf.LineAlignment = StringAlignment.Center;

            if (!Horizontal) e.Graphics.Clear(LyricShowSetting.LBackColor);
            if (EnabledShowText)
            {
                e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
                e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(LyricShowSetting.LHTextColor), new Rectangle(0, 0, this.Width, this.Height), sf);
                return;
            }

            for (int i = 0; i < lyric.Times.Count; i++)
            {
                if (i == lyric.Times.Count - 1) break;
                try
                {
                    if (Horizontal)
                    {
                        if (current >= lyric.Times[i] && current < lyric.Times[i + 1])
                        {
                            double timec = lyric.Times[i + 1] - lyric.Times[i];
                            double timec1 = current - lyric.Times[i];
                            timecb = timec1 / timec;

                            Size s = TextRenderer.MeasureText(lyric.Texts[i].Trim(), this.Font);
                            Point offset = new Point(this.Width / 2 - s.Width / 2, this.Height / 2 - s.Height / 2);
                            if (s.Width > this.Width)
                            {
                                offset.X = 0;
                                if (timecb * s.Width >= this.Width / 2)
                                    offset.X = -(int)(timecb * s.Width - this.Width / 2);
                                if (offset.X + s.Width < this.Width + 2) offset.X = this.Width - s.Width + 2;
                            }

                            e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
                            e.Graphics.DrawString(lyric.Texts[i], this.Font, new SolidBrush(LyricShowSetting.LHTextColor), offset);
                        }
                    }
                    else
                    {
                        int needy = (this.Height - 10) / 2 + bounds[nowline].Height / 2; //最终应显示的位置
                        temp = needy - bounds[nowline].Y; //偏移位置
                        double timec = lyric.Times[nowline + 1] - lyric.Times[nowline]; //两句歌词的时间差
                        double timec1 = current - lyric.Times[nowline]; //当前已经经过的时间
                        timecb = timec1 / timec; //已经经过的百分比
                        double topc = bounds[nowline + 1].Y - bounds[nowline].Y + bounds[nowline].Height / 2 - bounds[nowline + 1].Height / 2; //两句歌词的位置差
                        temp -= (int)(topc / timec * timec1); //得出平滑滚动位置
                        if (!dragging) drag = -temp + this.Height / 2;

                        e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
                        if (current >= lyric.Times[i] && current < lyric.Times[i + 1])
                        {
                            double cg = current - lyric.Times[i];
                            e.Graphics.DrawString(i == 0 ? "" : lyric.Texts[i], this.Font, new SolidBrush(LyricShowSetting.LHTextColor), new Rectangle(5, 0 + temp + bounds[i].Y, bounds[i].Width, bounds[i].Height), sf);
                            if (LyricShowSetting.LFade && cg >= 0 && cg <= 1)
                                e.Graphics.DrawString(i == 0 ? "" : lyric.Texts[i], this.Font, new SolidBrush(Color.FromArgb(255 - (int)(cg * 255), LyricShowSetting.LTextColor)), new Rectangle(5, 0 + temp + bounds[i].Top, bounds[i].Width, bounds[i].Height), sf);
                        }
                        else
                        {
                            //if (temp + bounds[i].Top + bounds[i].Height < 0) continue;
                            //if (temp + bounds[i].Top > this.Height) break;

                            double cg = current - lyric.Times[i + 1];
                            e.Graphics.DrawString(i == 0 ? "" : lyric.Texts[i], this.Font, new SolidBrush(LyricShowSetting.LTextColor), new Rectangle(5, 0 + temp + bounds[i].Y, bounds[i].Width, bounds[i].Height), sf);
                            if (LyricShowSetting.LFade && cg >= 0 && cg <= 1)
                                e.Graphics.DrawString(i == 0 ? "" : lyric.Texts[i], this.Font, new SolidBrush(Color.FromArgb(255 - (int)(cg * 255), LyricShowSetting.LHTextColor)), new Rectangle(5, 0 + temp + bounds[i].Y, bounds[i].Width, bounds[i].Height), sf);
                        }

                        if (dragging)
                        {
                            e.Graphics.DrawLine(new Pen(LyricShowSetting.LTextColor), 0, this.Height / 2, this.Width, this.Height / 2);
                            DateTime dt = new DateTime();
                            dt = dt.AddSeconds(CurrentProgress);
                            string t = dt.ToString("mm:ss");
                            e.Graphics.DrawString(t, this.Font, new SolidBrush(LyricShowSetting.LTextColor), new PointF(this.Width - (int)e.Graphics.MeasureString(t, this.Font).Width - 2, this.Height / 2 - this.Font.Height - 2));
                        }
                    }
                }
                catch { }
            }

            if (!Horizontal)
            {
                e.Graphics.FillRectangle(new LinearGradientBrush(new Rectangle(0, 0, this.Width, 30), LyricShowSetting.LBackColor, Color.FromArgb(0, LyricShowSetting.LBackColor), 90), new Rectangle(0, 0, this.Width, 30));
                e.Graphics.FillRectangle(new LinearGradientBrush(new Rectangle(0, this.Height - 30, this.Width, 30), Color.FromArgb(0, LyricShowSetting.LBackColor), LyricShowSetting.LBackColor, 90), new Rectangle(0, this.Height - 30, this.Width, 30));
            }

            //e.Graphics.DrawLine(Pens.Black, new Point(0, this.Height / 2), new Point(this.Width, this.Height / 2));
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            base.OnSizeChanged(e);
            if (!this.DesignMode)
            {
                SetLyric(lyric);
            }
        }

        protected override void OnFontChanged(EventArgs e)
        {
            base.OnFontChanged(e);
            SetLyric(lyric);
        }
        #endregion
    }
}
